/*
 * Copyright 2019 New Vector Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package im.vector.riotx.core.error

import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.failure.isInvalidPassword
import im.vector.matrix.android.api.session.identity.IdentityServiceError
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject
import javax.net.ssl.SSLException
import javax.net.ssl.SSLPeerUnverifiedException

interface ErrorFormatter {
    fun toHumanReadable(throwable: Throwable?): String
}

class DefaultErrorFormatter @Inject constructor(
        private val stringProvider: StringProvider
) : ErrorFormatter {

    override fun toHumanReadable(throwable: Throwable?): String {
        return when (throwable) {
            null                         -> null
            is IdentityServiceError      -> identityServerError(throwable)
            is Failure.NetworkConnection -> {
                when (throwable.ioException) {
                    is SocketTimeoutException     ->
                        stringProvider.getString(R.string.error_network_timeout)
                    is UnknownHostException       ->
                        // Invalid homeserver?
                        // TODO Check network state, airplane mode, etc.
                        stringProvider.getString(R.string.login_error_unknown_host)
                    is SSLPeerUnverifiedException ->
                        stringProvider.getString(R.string.login_error_ssl_peer_unverified)
                    is SSLException               ->
                        stringProvider.getString(R.string.login_error_ssl_other)
                    else                          ->
                        stringProvider.getString(R.string.error_no_network)
                }
            }
            is Failure.ServerError       -> {
                when {
                    throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN  -> {
                        // Special case for terms and conditions
                        stringProvider.getString(R.string.error_terms_not_accepted)
                    }
                    throwable.isInvalidPassword()                            -> {
                        stringProvider.getString(R.string.auth_invalid_login_param)
                    }
                    throwable.error.code == MatrixError.M_USER_IN_USE        -> {
                        stringProvider.getString(R.string.login_signup_error_user_in_use)
                    }
                    throwable.error.code == MatrixError.M_BAD_JSON           -> {
                        stringProvider.getString(R.string.login_error_bad_json)
                    }
                    throwable.error.code == MatrixError.M_NOT_JSON           -> {
                        stringProvider.getString(R.string.login_error_not_json)
                    }
                    throwable.error.code == MatrixError.M_THREEPID_DENIED    -> {
                        stringProvider.getString(R.string.login_error_threepid_denied)
                    }
                    throwable.error.code == MatrixError.M_LIMIT_EXCEEDED     -> {
                        limitExceededError(throwable.error)
                    }
                    throwable.error.code == MatrixError.M_THREEPID_NOT_FOUND -> {
                        stringProvider.getString(R.string.login_reset_password_error_not_found)
                    }
                    throwable.error.code == MatrixError.M_USER_DEACTIVATED   -> {
                        stringProvider.getString(R.string.auth_invalid_login_deactivated_account)
                    }
                    else                                                     -> {
                        throwable.error.message.takeIf { it.isNotEmpty() }
                                ?: throwable.error.code.takeIf { it.isNotEmpty() }
                    }
                }
            }
            is Failure.OtherServerError  -> {
                when (throwable.httpCode) {
                    HttpURLConnection.HTTP_NOT_FOUND ->
                        // homeserver not found
                        stringProvider.getString(R.string.login_error_no_homeserver_found)
                    else                             ->
                        throwable.localizedMessage
                }
            }
            else                         -> throwable.localizedMessage
        }
                ?: stringProvider.getString(R.string.unknown_error)
    }

    private fun limitExceededError(error: MatrixError): String {
        val delay = error.retryAfterMillis

        return if (delay == null) {
            stringProvider.getString(R.string.login_error_limit_exceeded)
        } else {
            // Ensure at least 1 second
            val delaySeconds = delay.toInt() / 1000 + 1
            stringProvider.getQuantityString(R.plurals.login_error_limit_exceeded_retry_after, delaySeconds, delaySeconds)
        }
    }

    private fun identityServerError(identityServiceError: IdentityServiceError): String {
        return stringProvider.getString(when (identityServiceError) {
            IdentityServiceError.OutdatedIdentityServer       -> R.string.identity_server_error_outdated_identity_server
            IdentityServiceError.OutdatedHomeServer           -> R.string.identity_server_error_outdated_home_server
            IdentityServiceError.NoIdentityServerConfigured   -> R.string.identity_server_error_no_identity_server_configured
            IdentityServiceError.TermsNotSignedException      -> R.string.identity_server_error_terms_not_signed
            IdentityServiceError.BulkLookupSha256NotSupported -> R.string.identity_server_error_bulk_sha256_not_supported
            IdentityServiceError.BindingError                 -> R.string.identity_server_error_binding_error
            IdentityServiceError.NoCurrentBindingError        -> R.string.identity_server_error_no_current_binding_error
        })
    }
}
